#include "divide_op.h"
#include "multiply_op.h"
#include "square_op.h"
#include "../constant.h"
#include "../../factory/factory.h"
#include "../../factory/divide_factory.h"
#include "../../factory/multiply_factory.h"
#include "../../factory/square_factory.h"
#include "../../factory/constant_factory.h"

Divide::Divide(Node* lhs, Node* rhs) : OpNode({lhs, rhs}){
}

vector<Node*> Divide::getGradNodes(Node* grad){
    Factory * factory;
    auto nodes = *getNodeIn();
    factory = new DivideFactory(grad, nodes[1]);
    Node* l_grad = factory -> createNode();
    delete factory;
    factory = new MultiplyFactory(grad, nodes[0]);
    Node* multiply = factory -> createNode();
    delete factory;
    factory = new SquareFactory(nodes[1]);
    Node* square = factory -> createNode();
    delete factory;
    factory = new DivideFactory(multiply, square);
    Node* divide = factory -> createNode();
    delete factory;
    Shape* shape = nodes[0] -> getOutShape();
    factory = new ConstantFactory(Matrix(shape -> rows, shape -> cols, -1));
    Node* neg_ones = factory -> createNode();
    delete factory;
    factory = new MultiplyFactory(neg_ones, divide);
    Node* r_grad = factory -> createNode();
    delete factory;
    return { l_grad, r_grad };
}

void Divide::compute(Context* context){
    auto nodes = *getNodeIn();
    context -> setFlowedData(this, 
            context -> getFlowedData(nodes[0]) / context -> getFlowedData(nodes[1]));
}
